﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using PropertyExpression.Common;

namespace ConsoleApplication81
{
    public static class SqlDataReaderExtensions
    {
        public static int GetInt32(this SqlDataReader it, string name)
        {
            return it.GetInt32(it.GetOrdinal(name));
        }
        
        public static Option<DateTime> GetOptionDateTime(this SqlDataReader it, string name)
        {
            var ordinal = it.GetOrdinal(name);
            return it.IsDBNull(ordinal) ? new Option<DateTime>() : it.GetDateTime(ordinal);
        }
    }

    public interface T001
    {
        int Id { get; }
        DateTime? CreationDate { get; }
    }

    class Program
    {
        static void Main()
        {
            SomeMathod(new DateTime(2000, 1, 1));
        }

        private static void SomeMathod(DateTime? date)
        {
            using (var connection = new SqlConnection(ConnectionString))
            {
                connection.Open();
                using (var command = connection.CreateCommand())
                {
                    var readerFunc = new {date, command}.Apply(p => {
                        var builder = new StringBuilder(@"
SELECT  Id, CreationDate
FROM    Post
WHERE   1 = 1");
                        if (date.HasValue)
                        {
                            builder.Append(@"
        AND CreationDate >= @date");
                            p.command.Parameters.AddWithValue("@date", p.date);
                        }
                        p.command.CommandText = builder.ToString();
                        return ReaderFunc.New(reader => new {
                            Id = reader.GetInt32("Id"),
                            CreationDate = reader.GetOptionDateTime("CreationDate")
                        });
                    });
                    using (var reader = command.ExecuteReader())
                        while (reader.Read())
                        {
                            var record = readerFunc(reader);
                        }
                }
            }
        }

        private static void SomeMathod2(DateTime? date)
        {
            var list = GetEnumerable(command => new {date, command}.Apply(p => {
                var builder = new StringBuilder(@"
SELECT  Id, CreationDate
FROM    Post
WHERE   1 = 1");
                if (date.HasValue)
                {
                    builder.Append(@"
        AND CreationDate >= @date");
                    p.command.Parameters.AddWithValue("@date", p.date);
                }
                p.command.CommandText = builder.ToString();
                return ReaderFunc.New<T001>();
            })).ToList();
        }

        private static IEnumerable<T> GetEnumerable<T>(Func<SqlCommand, Func<SqlDataReader, T>> func)
        {
            using (var connection = new SqlConnection(ConnectionString))
            {
                connection.Open();
                using (var command = connection.CreateCommand())
                {
                    var readerFunc = func(command);
                    using (var reader = command.ExecuteReader())
                        while (reader.Read())
                            yield return readerFunc(reader);
                }
            }
        }

        //private static void Test1()
        //{
        //    new {}.Apply(p => ReaderFunc.New(reader => new {}));
        //    new {}.Apply(p => Tuple.Create(ReaderFunc.New(reader => new {}), ReaderFunc.New(reader => new {})));
        //    new {}.Apply(p => QResult.New(ReaderFunc.New(reader => new {}), new {}));
        //    new {}.Apply(p => QResult.New(
        //        Tuple.Create(
        //            ReaderFunc.New(reader => new {}),
        //            ReaderFunc.New(reader => new {})),
        //        new {}));
        //    int test = new {test = 1}.Apply(p => QResult.New(
        //        new {},
        //        new {p.test})).Other.test;
        //}

        public static string ConnectionString
        {
            get { return @"Data Source=(local);Initial Catalog=Serverfault;Integrated Security=True"; }
        }
    }

    public static class CommandFunc
    {
        public static Func<SqlCommand, T> New<T>(Func<SqlCommand, T> func)
        {
            return func;
        }
    }

    public static class ReaderFunc
    {
        public static Func<SqlDataReader, T> New<T>(Func<SqlDataReader, T> func)
        {
            return func;
        }

        public static Func<SqlDataReader, T> New<T>()
        {
            throw new NotImplementedException();
        }
    }

    public static class Func
    {
        public static TResult Apply<T, TResult>(this T it, Func<T, TResult> func)
        {
            return func(it);
        }

        public static Func<T, TResult> New<T, TResult>(Func<T, TResult> func)
        {
            return func;
        }
    }

    public static class QResult
    {
        public static QResult<TReader, TOther> New<TReader, TOther>(TReader reader, TOther other)
        {
            return new QResult<TReader, TOther>(reader, other);
        }
    }

    public class QResult<TReader, TOther>
    {
        public readonly TReader Reader;
        public readonly TOther Other;

        public QResult(TReader reader, TOther other)
        {
            Reader = reader;
            Other = other;
        }
    }
}
